import java.io.*;
import java.net.*;
import java.util.*;


public class IRCClient {
	private Socket socket;
	private String host;
	private int port;
	private String nick;
	private String user;
	private String mode;
	private String lastName;
	private String firstName;
	private OutputStream out;;
	private Input in;
	private Boolean connected;
	public IRCCommandCreator commandc;
	private IRCCommandEvaluator evaluator;

	private Map<String, GUIchannel> channels;
	private Map<String, GUIchannel> privmsgs;
	private GUI gui;


	public IRCClient(GUI gui) {
		
		commandc = new IRCCommandCreator(this);
		evaluator = new IRCCommandEvaluator(this);
		channels = new TreeMap<String, GUIchannel>();
		privmsgs = new TreeMap<String, GUIchannel>();
		this.gui = gui;
		in = new Input(this);
		loadConfig();
		
//		host = "irc.atw-inter.net";
//		port = 6667;
//		nick = "nirctest";
		user = "guest";
		mode = "0";
//		firstName = "Lth";
//		lastName = "Proj";
		connected = false;
		
	}

	public void setNick(String nick) {
		if (!connected){
			this.nick = nick;
		}else {
			commandc.nick(nick);
		}
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getNick() {
		return nick;
	}
	public String getFirstName() {
		return firstName;
	}
	public String getLastName() {
		return lastName;
	}
	
	public String getHost() {
		return host;
	}
	
	public String getPort() {
		return Integer.toString(port);
	}
	
	public void setHost(String host) {
		this.host = host;
	}
	
	public void setPort(String port) {
		this.port = Integer.parseInt(port);
	}
	
	protected void finalize() throws Throwable {
		saveConfig();
	}
	
	public void connect() {
		if (!connected) {
			try {
				socket = new Socket(host, port);
				out = socket.getOutputStream();
				connected = true;
				commandc.nick(nick);
				commandc.user(user, mode, firstName, lastName);
				in.setSocket(socket);
				in.start();
			} catch (Exception e) {
				printToSys("Could not create socket for " + host + ":" + port);
			}
		}else {
			printToSys("You are already connected.");
		}
	}
	
	public void disconnect() {
		if(connected) {
			try {
				socket.close();
				onDisconnect();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public void onDisconnect() {
		if(connected){
			in.interrupt();
			connected = false;
			removeAllChannels();
			printToSys("Disconnected");
		}
	}
	
	public void addChannel(String channel) {
		channels.put(channel, gui.joinChannel(channel));
	}
	
	public void removeChannel(String channel) {
		gui.closeChannel(channel);
		channels.remove(channel);
	}
	
	public void removeAllChannels() {
		Iterator<String> itr = channels.keySet().iterator();
		while(itr.hasNext()){
			removeChannel(itr.next());
		}
	}
	
	public void leaveChannel(String channel) {
		if (channels.containsKey(channel)) {
			commandc.part(channel, "gone");
			removeChannel(channel);
		} else if (privmsgs.containsKey(channel)){
			removePrivmsg(channel);
		}
	}
	
	public boolean containsChannel(String channel) {
		return channels.containsKey(channel);
	}
	
	public boolean containsPrivmsg(String user) {
		return privmsgs.containsKey(user);
	}
	
	public void addPrivmsg(String user) {
		privmsgs.put(user, gui.joinChannel(user));
	}
	
	public void removePrivmsg(String user) {
		gui.closeChannel(user);
		privmsgs.remove(user);
	}
	
	public void addUser(String channel, String user) {
		if (channels.containsKey(channel)){
			channels.get(channel).addUser(user);
		}
		else{
			printToSys("unrecognized channel: " + channel + " join: " + user);
		}
	}
	
	public void removeUser(String channel, String user) {
		if (channels.containsKey(channel)){
			channels.get(channel).removeUser(user);
		}
		else{
			printToSys("unrecognized channel: " + channel + " part: " + user);
		}
	}
	
	public void setTopic(String channel, String text) {
		//TODO implement when a client has the possibility to have a topic.
	}
	
	public void changeUser(String oldName, String newName) {
		Iterator<String> itr = channels.keySet().iterator();
		while(itr.hasNext()){
			String key = itr.next();
			channels.get(key).changeUser(oldName, newName);
		}
		if (privmsgs.containsKey(oldName)){
			privmsgs.put(newName, privmsgs.get(oldName));
			privmsgs.get(newName).setName(newName);
			gui.changeTabTitle(oldName, newName);
			privmsgs.remove(oldName);
			//TODO change name of conversation.
		}
	}
	
	public void removeUser(String user){
		Iterator<String> itr = channels.keySet().iterator();
		while(itr.hasNext()){
			String key = itr.next();
			if (channels.get(key).hasUser(user)){
				removeUser(key, user);
			}
		}
	}
	
	public void startPrivChat(String user) {
		user = removeAtPlus(user);
		if (!privmsgs.containsKey(user)){
			addPrivmsg(user);
		}
	}
	
	public void send(String message) {
		if (connected) {
			try {
				out.write(message.getBytes());
				out.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}else {
			printToSys("Not connected: " + message);
		}
	}
	
	public void printToChannel(String channel, String msg) {
		if (channels.containsKey(channel)){
			channels.get(channel).appendTextArea(msg);
		}
		else{
			printToSys("unrecognized channel: " + channel + " msg: " + msg);
		}
	}
	
	public void printToPrivmsg(String user, String msg) {
		if (!privmsgs.containsKey(user)){
			addPrivmsg(user);
		}
		privmsgs.get(user).appendTextArea(msg);
	}
	
	public void printToSys(String msg) {
		gui.appendTextArea(msg);
	}
	
	public void evalCommand(IRCCommand cmd){
		evaluator.evaluation(cmd);
	}
	
	public void changeOpVoice(String channel, String user, char mode, boolean sign){
		if (channels.containsKey(channel)){
			GUIchannel chan = channels.get(channel);
			if (chan.hasUser(user)){
				if (mode == 'o' || mode == 'O'){
					if (sign) {
						chan.addOp(user);
					}else {
						chan.removeOp(user);
					}
				}else if (mode == 'v') {
					if (sign) {
						chan.addVoice(user);
					}else {
						chan.removeVoice(user);
					}
				}
			}
		}
	}
	
	public void loadConfig() {
		Properties configFile = new Properties();
		try {
			String userdir = System.getProperty("user.dir");
			configFile.load(new FileInputStream(userdir + "/config.properties"));
			host = configFile.getProperty("host");
			String p = configFile.getProperty("port");
			if (p != null){
				port = Integer.parseInt(p);
			}
			else port = 0;
			nick = configFile.getProperty("nick");
			user = configFile.getProperty("user");
			mode = configFile.getProperty("mode");
			firstName = configFile.getProperty("firstName");
			lastName = configFile.getProperty("lastName");
		} catch (Exception e) {
		}
		if (host == null) host = "";
		if (nick == null) nick = "";
		if (user == null) user = "";
		if (mode == null) mode = "0";
		if (firstName == null) firstName = "";
		if (lastName == null) lastName = "";
	}
	
	public void saveConfig() {
		Properties configFile = new Properties();
		configFile.setProperty("host", host);
		configFile.setProperty("port", Integer.toString(port));
		configFile.setProperty("nick", nick);
		configFile.setProperty("user", user);
		configFile.setProperty("mode", mode);
		configFile.setProperty("firstName", firstName);
		configFile.setProperty("lastName", lastName);
		try {
			String userdir = System.getProperty("user.dir");
			configFile.store(new FileOutputStream(new File(userdir + "/config.properties")), "");
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
		
	public String removeAtPlus(String user) {
		return user.replace("@", "").replace("+", "");
	}
}
